package uz.rt.dicom.data.element;

import uz.rt.dicom.ValueRepresentation;
import uz.rt.dicom.data.ByteElement;
import uz.rt.dicom.data.ComparableList;
import uz.rt.dicom.io.DicomIOException;
import uz.rt.dicom.io.DicomInputStream;
import uz.rt.dicom.tag.DicomTag;

import java.io.IOException;

public class Date extends ByteElement<ComparableList<String>> implements Comparable<Date>
{
// ------------------------------ FIELDS ------------------------------

    /** Stores all date related parameters. */
    private DateGroup[] dateGroup = null;

// --------------------------- CONSTRUCTORS ---------------------------

    public Date(DicomTag tag)
    {
        super(tag);
        setMaxLength(8);
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    public DateGroup[] getDateGroup()
    {
        return dateGroup;
    }

    public void setDateGroup(DateGroup[] dateGroup)
    {
        this.dateGroup = dateGroup;
    }

    public ComparableList<String> getValue()
    {
        return value;
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface Comparable ---------------------

    /**
     * The function compares only the following parameters in order:
     * <ul>
     * <li>Dicom tag</li>
     * <li>Value multiplicity</li>
     * <li>Comparable list of data fields</li>
     * </ul>
     *
     * @param o A Date element containing a value of the ComparableList datatype
     * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
     *         the specified object.
     */
    public int compareTo(Date o)
    {
        if (!tag.equals(o.getTag())) return tag.compareTo(o.getTag());
        if (!vm.equals(o.getVm())) return vm.compareTo(o.getVm());
        return value.compareTo(o.getValue());
    }

// --------------------- Interface IDataElement ---------------------

    @Override
    public int getLength()
    {
        return value.size();
    }

    @Override
    public byte[] getVR()
    {
        return ValueRepresentation.DA;
    }

    @Override
    public ValueRepresentation getValueRepresentation()
    {
        return ValueRepresentation.Date;
    }

    public void read(DicomInputStream dicomInputStream, int length) throws IOException
    {
        if (length % maxLength != 0)
        {
            value = null;
            throw new DicomIOException(
                    "The modulus of the length and maximum length should be zero.");
        }
        value = null;
        value = new ComparableList<String>();
        String s = dicomInputStream.readString(length);
        if (s.length() != 8)
            throw new DicomIOException("Expected a date character string with a length of 8 characters.");
        String[] groups = new String[1];
        groups[0] = s;
        if (groups.length < vm.getMin() || groups.length > vm.getMax())
        {
            value = null;
            throw new DicomIOException(
                    "Value multiplicity violation: group length = " + groups.length +
                            ", limits are [" + vm.toString() + "]");
        }
        dateGroup = new DateGroup[groups.length];
        int index = 0;
        for (String group : groups)
        {
            for (int i = 0; i < groups.length; i++)
            {
                if (!Character.isDigit(group.charAt(i)))
                {
                    value = null;
                    dateGroup[i] = null;
                    throw new DicomIOException(
                            "Character \"" + group.charAt(i) + " \" is not allow. Allowed characters are: [0-9]");
                }
            }
            if (dateGroup[index] == null) dateGroup[index] = new DateGroup();
            dateGroup[index].year = Integer.valueOf(group.substring(0, 4));
            dateGroup[index].month = Integer.valueOf(group.substring(4, 6));
            dateGroup[index].day = Integer.valueOf(group.substring(6));
            value.add(group);
            index++;
        }
    }

// -------------------------- INNER CLASSES --------------------------

    /** Class stores all date related parameters. */
    public class DateGroup
    {
        @Override
        public boolean equals(Object o)
        {
            if (this == o) return true;
            if (!(o instanceof DateGroup)) return false;

            DateGroup dateGroup = (DateGroup) o;

            return day == dateGroup.day && month == dateGroup.month && year == dateGroup.year && !(format != null ? !format
                    .equals(dateGroup.format) : dateGroup.format != null);
        }

        @Override
        public int hashCode()
        {
            int result = format != null ? format.hashCode() : 0;
            result = 31 * result + day;
            result = 31 * result + month;
            result = 31 * result + year;
            return result;
        }

        /** Data format */
        private String format = "YYYYMMDD";
        /** Day of the month */
        private int day = Integer.MIN_VALUE;
        /** Month of the year */
        private int month = Integer.MIN_VALUE;
        /** Year */
        private int year = Integer.MIN_VALUE;

        /**
         * Get the day based on the internal value.
         * <p/>
         * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
         *
         * @return Day
         */
        public int getDay()
        {
            return day;
        }

        /**
         * Get the month based on the internal value.
         * <p/>
         * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
         *
         * @return Month
         */
        public int getMonth()
        {
            return month;
        }

        /**
         * Get the year based on the internal value.
         * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
         *
         * @return Year
         */
        public int getYear()
        {
            return year;
        }
    }
}
